#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <windows.h>
#include <commctrl.h>
#include <math.h>

#include "../util.h"
#include "../obd_pid.h"
#include "../config.h"
#include "../serial.h"
#include "../event.h"
#include "../util.h"
#include "resource.h"

static struct config config;
static int event_cnt;

struct configfile {
	FILE *fp;
	char *filename;
	struct config *config;
	uint32_t length;
};

static struct configfile configfile;
static struct serial_dev sdev;

BOOL CALLBACK callback(HWND, UINT, WPARAM, LPARAM); 

void init_gui(HWND);

int add_event( HWND hWnd );
void delete_event( HWND ); /*Event number to delete*/
int save_config(HWND, struct configfile *);
int load_config(HWND, struct configfile *);
int open_cfg(HWND, struct configfile*, char*);
void update_crc(struct config *conf);

int WINAPI
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine,
    int iCmdShow) 
{
	MSG msg;
	HWND hDlg;

	OleInitialize(NULL);

	hDlg = CreateDialogParam(hInstance,
	    MAKEINTRESOURCE(IDD_DIALOG1), NULL, callback, (LPARAM)
	    hInstance);
	if (hDlg <= 0) {
		MessageBox(NULL, "Error creating dialog box.", "Error",
		    MB_OK | MB_ICONERROR);
		exit(1);
	}
	ShowWindow(hDlg, SW_SHOWNORMAL);
	UpdateWindow(hDlg);

	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	
	OleUninitialize();

	return msg.wParam;
}

BOOL CALLBACK
callback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	int total;
	char buf[80];
	char subbuf[32];
	int r;
	int interval;
	int pid;
	
	switch(uMsg) {
	case WM_INITDIALOG:
		// lParam is hInstance
		init_gui( hwnd );
		#if 0
		memset(&pibfile, 0, sizeof(pibfile));
		#endif
		{
			SetDlgItemText( hwnd, IDC_BAUD, "57600" );
			HICON hicon;
			hicon = LoadIcon((HINSTANCE) lParam,
			    MAKEINTRESOURCE(IDI_ICON1)); 
			if (hicon == NULL) {
				MessageBox(hwnd, "Could not set icon.",
				    "Error", MB_OK | MB_ICONERROR);
				DestroyWindow(hwnd);
			}
			SendMessage(hwnd, WM_SETICON, ICON_SMALL,
			    (LPARAM) hicon);
			SendMessage(hwnd, WM_SETICON, ICON_BIG,
			    (LPARAM) hicon);
		}
		break;

	case WM_COMMAND:
		if (HIWORD(wParam) == BN_CLICKED) {
			switch(LOWORD(wParam)) {
			case IDC_BTNPIDADD:
				add_event(hwnd);
				break;
			case IDC_PIDDEL:
				delete_event(hwnd);
				break;
			case IDC_BTNOPEN:
				if(open_cfg(hwnd, &configfile, "rb") == -1)
					break;
				if(load_config(hwnd, &configfile)  == -1)
					break;
				break;
			case IDC_BTNUPLOAD:
				GetDlgItemText( hwnd, IDC_COMSEL, buf, sizeof(buf) );
				if (init_serial(&sdev, buf) == -1){
					MessageBox(hwnd, "Could not open serial port.", "Error",  MB_OK | MB_ICONERROR);
					break;
				}
				
				strcpy(buf, "!!!\recho off\r");
				r = write_ser(&sdev, buf, strlen(buf));
	
				if (r != strlen(buf)) {
					close_serial(&sdev);
					MessageBox(hwnd, "Could not open serial port.", "Error",  MB_OK | MB_ICONERROR);
					break;
				}
				
				ser_flush(&sdev);
				
				strcpy(buf, "write_cfg\r");
				r = write_ser(&sdev, buf, strlen(buf));
				if (r != strlen(buf)) {
					close_serial(&sdev);
					MessageBox(hwnd, "Could not open serial port.", "Error",  MB_OK | MB_ICONERROR);
					break;
				}
				
				total = 0;
				uint8_t *p = (uint8_t *) &config;
				while (total < sizeof(config)) {
					r = sizeof(config) - total;

					r = write_ser(&sdev, p, r);
					if (r == 0) {
						close_serial(&sdev);
						MessageBox(hwnd, "Could not open serial port.", "Error",  MB_OK | MB_ICONERROR);
						break;
					} else {
						total += r;
						p += r;
					}
				}
				
				r = read_ser(&sdev, buf, sizeof(buf));
				buf[r] = '\0';
				if (r == 0) {
					close_serial(&sdev);
					MessageBox(hwnd, "Could not open serial port.", "Error",  MB_OK | MB_ICONERROR);
					break;
				}

				if (!strncmp(buf, "OK", 2)) {
					MessageBox(hwnd, "Device accepted configuration!", "Success!",  MB_OK );
				} else {
					MessageBox(hwnd, "Device Rejected Configuration.", "Error", MB_OK|MB_ICONERROR);
				}
				
				close_serial(&sdev);
				
				break;
			case IDC_BTNDOWNLOAD:
				GetDlgItemText( hwnd, IDC_COMSEL, buf, sizeof(buf) );
				if (init_serial(&sdev, buf) == -1){
					MessageBox(hwnd, "Could not open serial port.", "Error",  MB_OK | MB_ICONERROR);
					break;
				}
				
				strcpy(buf, "!!!\recho off\r");
				r = write_ser(&sdev, buf, strlen(buf));
	
				if (r != strlen(buf)) {
					close_serial(&sdev);
					MessageBox(hwnd, "Could not open serial port.", "Error",  MB_OK | MB_ICONERROR);
					break;
				}
				
				ser_flush(&sdev);
				
				strcpy(buf, "read_cfg\r");
				r = write_ser(&sdev, buf, strlen(buf));
				if (r != strlen(buf)) {
					close_serial(&sdev);
					MessageBox(hwnd, "Could not open serial port.", "Error",  MB_OK | MB_ICONERROR);
					break;
				}
				
				total = 0;
				while (total < sizeof(config)) {
					r = sizeof(config) - total;
					if (r > sizeof(buf))
						r = sizeof(buf);

					r = read_ser(&sdev, buf, r);
					if (r == 0) {
						close_serial(&sdev);
						MessageBox(hwnd, "Could not open serial port.", "Error",  MB_OK | MB_ICONERROR);
						break;
					} else {
						memcpy(&config + total, buf, r);
						total += r;
					}
				}

				if (!valid_config(&config)) {
					MessageBox(hwnd, "Error. Invalid config read.", "Error",  MB_OK | MB_ICONERROR);
					break;
				} else {
					MessageBox(hwnd, "Read a good config!", "Success",  MB_OK);
				}
				
				SendDlgItemMessage(hwnd, IDC_PIDLIST, LB_RESETCONTENT, 0 , 0);
				
				event_cnt = 0;
					int i;
					for(i=0; i< NUM_LOG_SLOTS; ++i)
					{
						pid = config.event[i].pid;
						interval = config.event[i].interval;
						
						if( strlen(pid_name[pid]) > 30 )
						{
							strncpy( subbuf, pid_name[pid], 30 );
							strncat( subbuf, "...", sizeof(subbuf) );
						}
						else {
							strncpy( subbuf, pid_name[pid], sizeof(subbuf));
						}
					
						if( interval > 0 ) {
							snprintf(buf,sizeof(buf),"(PID %02x) %s / %0.1f s", pid, subbuf, ((double)interval)*0.5 );
							SendDlgItemMessage(hwnd, IDC_PIDLIST, LB_INSERTSTRING, event_cnt, (LPARAM) buf );
							config.event[event_cnt].mode = 1;
						
							event_cnt++;
						}

					}
				close_serial(&sdev);
				
				break;
			case IDC_BTNSAVE:
				configfile.config = &config;
				
				if( open_cfg(hwnd,&configfile,"wb") == -1 )
					break;
					
				if( save_config(hwnd, &configfile) == -1 )
					break;
				break;
				
			case IDC_CLEARALL:
				memset(&config,0,sizeof( struct config ) );
				event_cnt = 0;
				SendDlgItemMessage(hwnd, IDC_PIDLIST, LB_RESETCONTENT, 0 , 0);
				break;
			}
	}
		break;

	case WM_CLOSE:
		DestroyWindow(hwnd);
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		break;

	default:
		return FALSE;
	}

	return TRUE;
}

void 
init_gui(HWND hWnd)
{
	int i;
	char buf[64];
	
	i = 0;
	while( i<=0x2F )
	{
		snprintf(buf, sizeof(buf),"(%02X) %s", i, pid_name[i] );
		SendDlgItemMessage( hWnd, IDC_PIDADD,  CB_INSERTSTRING, i, (LPARAM) buf );
		i++;
	}

}

int 
add_event( HWND hWnd ) /*Adds an event to the dconfig and the list box*/
{
	char buf[128];
	char subbuf[40];
	int interval;
	double time;
	int pid;
	
	if( event_cnt == NUM_LOG_SLOTS )
		return -1;
	
	GetDlgItemText( hWnd, IDC_PIDINTERVAL, buf, sizeof(buf) );
	time = atof( buf );
	
	interval = rint( time/0.5 );
	
	if(interval == 0 )
		interval = 1;
	
	pid = SendDlgItemMessage( hWnd, IDC_PIDADD,  CB_GETCURSEL, 0, 0 );
	
	if( pid < 0 || interval < 0 )
		return -1;
	
	if( strlen(pid_name[pid]) > 30 )
	{
		strncpy( subbuf, pid_name[pid], 30 );
		strncat( subbuf, "...", sizeof(subbuf) );
	}
	else {
		strncpy( subbuf, pid_name[pid], sizeof(subbuf));
	}
	
	snprintf(buf,sizeof(buf),"(PID %02x) %s / %0.1f s", pid, subbuf, ((double)interval)*0.5 );
	SendDlgItemMessage(hWnd, IDC_PIDLIST, LB_INSERTSTRING, event_cnt, (LPARAM) buf );
	
	config.event[event_cnt].interval = interval;
	config.event[event_cnt].mode = 1;
	config.event[event_cnt].pid = pid;
	
	event_cnt++;
	
	return 0;
}

void 
delete_event( HWND hWnd ) /*Event number to delete*/
{
	int sel;
	int i;
	
	sel = SendDlgItemMessage(hWnd, IDC_PIDLIST, LB_GETCURSEL, 0 , 0);
	
	if( sel < 0 )
		return;
		
	SendDlgItemMessage(hWnd, IDC_PIDLIST, LB_DELETESTRING, (WPARAM) sel, 0 );
	event_cnt--;
	
	for(i=sel; i < event_cnt; ++i)
	{
		config.event[i].interval = config.event[i+1].interval;
		config.event[i].mode = config.event[i+1].mode;
		config.event[i].pid = config.event[i+1].pid;
	}
	
	for(i=event_cnt; i < NUM_LOG_SLOTS; ++i)	
	{
		config.event[i].interval = 0;
		config.event[i].mode = 0;
		config.event[i].pid = 0;
	}
}

int
open_cfg(HWND hwnd, struct configfile *cfgf, char *perm)
{
	char buf[MAX_PATH];
	OPENFILENAME ofn;
	int ret;

	memset(&ofn, 0, sizeof(ofn));
	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = hwnd;
	ofn.lpstrFile = buf;
	ofn.nMaxFile = sizeof(buf);
	ofn.lpstrFile[0] = '\0';
	ofn.lpstrFilter = "CFG Files (*.cfg)\0*.cfg\0All Files (*.*)\0*.*\0";
	ofn.lpstrCustomFilter = NULL;
	ofn.nFilterIndex = 0;
	ofn.lpstrFileTitle = NULL;
	ofn.lpstrInitialDir = NULL;
	ofn.lpstrTitle = NULL;
	ofn.Flags = OFN_FILEMUSTEXIST;
	ofn.lpstrDefExt = "cfg";

	cfgf->length = sizeof( struct config );
	
	if (strchr(perm, 'r') != NULL)
		ret = GetOpenFileName(&ofn);
	else {
		if (cfgf->config == NULL) {
			MessageBox(hwnd, "Nothing to save.", "Error",
			    MB_OK | MB_ICONERROR);
			return -1;
		}
		ret = GetSaveFileName(&ofn);
	}

	if (ret == TRUE) {
		cfgf->fp = fopen(buf, perm);
		if (cfgf->fp == NULL) {
			MessageBox(hwnd, "Could not open CFG file.",
			    "Error", MB_OK | MB_ICONERROR);
			return -1;
		}
		if (cfgf->filename != NULL)
			free(cfgf->filename);
		cfgf->filename = strdup(buf);

		return 0;
	}

	return -1;
}

int 
save_config(HWND hWnd, struct configfile * file)
{
	int r;
	
	struct config *config;
	config = file->config;
	
	update_crc(config);
	
	if (!valid_config(config)) {
		MessageBox(hWnd, "Configuration File Invalid.", "Error",
		    MB_OK | MB_ICONERROR);
		fclose(file->fp);
		return -1;
	}
	
	r = fwrite(config, 1, sizeof(struct config), file->fp);
	
	if (r != sizeof(struct config)) {
		MessageBox(hWnd, "Failed to write configuration file!", "Error",
		    MB_OK | MB_ICONERROR);
		fclose(file->fp);
		return -1;
	}
	
	fclose(file->fp);
	
	return r;
	
}

int 
load_config(HWND hWnd, struct configfile * file)
{
	char buf[128];
	char subbuf[40];
	int r;
	int i;
 	struct config tmpconfig;
	int pid;
	int interval;
	
	r = fread(&tmpconfig, 1, sizeof(struct config),file->fp);

	if (!valid_config(&tmpconfig)) {
		MessageBox(hWnd, "Invalid configuration file!", "Error",
		    MB_OK | MB_ICONERROR);
		fclose(file->fp);
		return -1;
	}
	
	SendDlgItemMessage(hWnd, IDC_PIDLIST, LB_RESETCONTENT, 0 , 0);
	
	memset(&config,0,sizeof(config));
	event_cnt = 0;
	
	for(i=0; i< NUM_LOG_SLOTS; ++i)
	{
		pid = tmpconfig.event[i].pid;
		interval = tmpconfig.event[i].interval;
		
		if( strlen(pid_name[pid]) > 30 )
		{
			strncpy( subbuf, pid_name[pid], 30 );
			strncat( subbuf, "...", sizeof(subbuf) );
		}
		else {
			strncpy( subbuf, pid_name[pid], sizeof(subbuf));
		}
	
		if( interval > 0 ) {
		snprintf(buf,sizeof(buf),"(PID %02x) %s / %0.1f s", pid, subbuf, ((double)interval)*0.5 );
		SendDlgItemMessage(hWnd, IDC_PIDLIST, LB_INSERTSTRING, event_cnt, (LPARAM) buf );
	
		config.event[event_cnt].interval = interval;
		config.event[event_cnt].mode = 1;
		config.event[event_cnt].pid = pid;
	
		event_cnt++;
		}

	}
	
	fclose(file->fp);
	return 0;
	
}

void
update_crc(struct config *conf)
{
	uint8_t *p;
	uint16_t crc;
	int i;

	p = (uint8_t *) conf;

	conf->crc = 0;
	crc = 0xffff;
	for (i = 0; i < sizeof(*conf); ++i) {
		crc = crc16_update(crc, *p);
		++p;
	}

	conf->crc = crc;
}
